package com.anttribe.components.db.metadata;

import java.text.MessageFormat;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import com.anttribe.components.db.metadata.constants.Constants;
import com.anttribe.components.db.metadata.supplier.DatabaseSupplier;
import com.anttribe.components.db.metadata.supplier.mysql.MySqlDatabaseSupplier;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 数据库方言
 *
 * @author zhaoyong
 * @date 2020-11-28
 */
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum Dialect {

    MYSQL("com.mysql.jdbc.Driver", MySqlDatabaseSupplier.class) {

        private static final String JDBC_URL_TEMPLATE = "jdbc:mysql://{0}:{1}/{2}?{3}";

        private final Properties defaultDbConnProps = new Properties();
        {
            defaultDbConnProps.setProperty("useUnicode", Constants.MYSQL.DEFAULT_USE_UNICODE);
            defaultDbConnProps.setProperty("characterEncoding", Constants.MYSQL.DEFAULT_CHARACTER_ENCODING);
            defaultDbConnProps.setProperty("useInformationSchema", Constants.MYSQL.DEFAULT_USE_INFORMATION_SCHEMA);
            defaultDbConnProps.setProperty("serverTimezone", Constants.MYSQL.DEFAULT_SERVER_TIMEZONE);
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema) {
            return this.buildJdbcUrl(host, port, schema, new Properties());
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            defaultDbConnProps.putAll(dbConnProps);
            return MessageFormat.format(JDBC_URL_TEMPLATE, host, port, schema,
                Dialect.buildDBConnectProperties(defaultDbConnProps));
        }
    },
    MARIADB("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    ORACLE("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    SQL_SERVER2005("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    SQL_SERVER("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    POSTGRE_SQL("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    HSQL("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    DB2("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    SQLITE("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    H2("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    DM("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    XU_GU("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    KINGBASE_ES("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    PHOENIX("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    CACHEDB("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    },
    OTHER("", null) {
        @Override
        public String buildJdbcUrl(String host, String port, String database) {
            return null;
        }

        @Override
        public String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps) {
            return null;
        }
    };

    /**
     * 数据库driver
     */
    private String driverClass;

    /**
     * 数据库Supplier
     */
    private Class<? extends DatabaseSupplier> supplierClass;

    /**
     * 构造数据库连接url
     *
     * @param host
     *            主机名/ip
     * @param port
     *            端口
     * @param schema
     *            数据库名
     * @return url
     */
    public abstract String buildJdbcUrl(String host, String port, String schema);

    /**
     * 构造数据库连接url
     *
     * @param host
     *            主机名/ip
     * @param port
     *            端口
     * @param schema
     *            数据库名
     * @param dbConnProps
     *            连接参数
     * @return url
     */
    public abstract String buildJdbcUrl(String host, String port, String schema, Properties dbConnProps);

    /**
     * 构造数据库连接属性
     *
     * @param dbProps
     *            Properties
     * @return String
     */
    private static String buildDBConnectProperties(Properties dbProps) {
        String dbConnectPropsStr = "";
        if (!MapUtils.isEmpty(dbProps)) {
            List<String> kvList = dbProps.entrySet().stream()
                .map(e -> e.getKey() + "=" + ObjectUtils.defaultIfNull(e.getValue(), "")).collect(Collectors.toList());
            dbConnectPropsStr = String.join("&", kvList);
        }
        return dbConnectPropsStr;
    }

    /**
     * 根据jdbc连接url获取数据库方言
     *
     * @param jdbcUrl
     * @return
     */
    public static Dialect decideDialect(String jdbcUrl) {
        if (StringUtils.containsAny(":mysql:", ":cobar:")) {
            return MYSQL;
        } else if (jdbcUrl.contains(":mariadb:")) {
            return MARIADB;
        } else if (jdbcUrl.contains(":oracle:")) {
            return ORACLE;
        } else if (jdbcUrl.contains(":sqlserver:") || jdbcUrl.contains(":microsoft:")) {
            return SQL_SERVER2005;
        } else if (jdbcUrl.contains(":sqlserver2012:")) {
            return SQL_SERVER;
        } else if (jdbcUrl.contains(":postgresql:")) {
            return POSTGRE_SQL;
        } else if (jdbcUrl.contains(":hsqldb:")) {
            return HSQL;
        } else if (jdbcUrl.contains(":db2:")) {
            return DB2;
        } else if (jdbcUrl.contains(":sqlite:")) {
            return SQLITE;
        } else if (jdbcUrl.contains(":h2:")) {
            return H2;
        } else if (jdbcUrl.contains(":dm:")) {
            return DM;
        } else if (jdbcUrl.contains(":xugu:")) {
            return XU_GU;
        } else if (jdbcUrl.contains(":kingbase:") || jdbcUrl.contains(":kingbase8:")) {
            return KINGBASE_ES;
        } else if (jdbcUrl.contains(":phoenix:")) {
            return PHOENIX;
        } else if (jdbcUrl.contains(":Cache:")) {
            return CACHEDB;
        } else {
            return OTHER;
        }
    }

}
